import tkinter as tk
from tkinter import messagebox, scrolledtext, Menu
import requests
import threading
import queue
import time

# Путь к файлу с токенами
TOKEN_FILE_PATH = "C:\\Users\\1\\Desktop\\VS 2022\\Очистка ВК\\токены для очистки.txt"
VERSION = "5.131"

# Флаг остановки
stop_flag = False

# Блокировка для синхронизации потоков
lock = threading.Lock()

# Очередь сообщений для обновления GUI из потоков
message_queue = queue.Queue()

# Переменные для отслеживания количества аккаунтов
total_accounts = 0
processed_accounts = 0
remaining_accounts = 0

# Переменные для отслеживания общего количества элементов
total_posts = 0
total_photos = 0
total_messages = 0

# Список для хранения статистики по каждому аккаунту
accounts_statistics = []

# Чтение токенов из файла
def read_tokens():
    try:
        with open(TOKEN_FILE_PATH, 'r', encoding='utf-8') as file:
            tokens = [line.strip() for line in file if line.strip()]
            if not tokens:
                raise ValueError("Файл с токенами пуст.")
            return tokens
    except Exception as e:
        messagebox.showerror("Ошибка", f"Не удалось прочитать токены: {e}")
        return []

# Создание главного окна
root = tk.Tk()
root.title("Очистка аккаунта VK")
root.geometry("800x700")

# Переменные для автопрокрутки в текстовых полях
auto_scroll_log = tk.BooleanVar(value=True)
auto_scroll_statistics = tk.BooleanVar(value=True)

# Получение токенов
TOKENS = read_tokens()
total_accounts = len(TOKENS)
remaining_accounts = total_accounts

if not TOKENS:
    exit()

# Функции для обновления меток
def update_account_labels():
    label_total_accounts_value.config(text=str(total_accounts))
    label_processed_accounts_value.config(text=str(processed_accounts))
    label_remaining_accounts_value.config(text=str(remaining_accounts))

def update_total_items_labels():
    label_total_posts_value.config(text=str(total_posts))
    label_total_photos_value.config(text=str(total_photos))
    label_total_messages_value.config(text=str(total_messages))

# Функция для получения информации о пользователе
def get_user_info(token):
    try:
        user_info_response = requests.get(
            'https://api.vk.com/method/users.get',
            params={
                'access_token': token,
                'v': VERSION,
                'fields': 'photo_id'
            }
        ).json()

        user_info = user_info_response['response'][0]
        user_id = user_info['id']
        user_name = f"{user_info['first_name']} {user_info['last_name']}"

        # Получение количества постов
        posts_response = requests.get(
            'https://api.vk.com/method/wall.get',
            params={
                'access_token': token,
                'v': VERSION,
                'owner_id': user_id,
                'count': 1
            }
        ).json()

        posts_count = posts_response['response']['count'] if 'response' in posts_response else 0

        # Получение количества фотографий
        photos_response = requests.get(
            'https://api.vk.com/method/photos.getAll',
            params={
                'access_token': token,
                'v': VERSION,
                'owner_id': user_id,
                'count': 1
            }
        ).json()

        photos_count = photos_response['response']['count'] if 'response' in photos_response else 0

        # Получение количества сообщений
        messages_response = requests.get(
            'https://api.vk.com/method/messages.getConversations',
            params={
                'access_token': token,
                'v': VERSION,
                'count': 1
            }
        ).json()

        messages_count = messages_response['response']['count'] if 'response' in messages_response else 0

        return user_name, user_id, posts_count, photos_count, messages_count

    except Exception as e:
        return 'Ошибка', 0, 0, 0, 0

# Функция для обновления статистики по одному аккаунту
def update_statistics_for_account(token):
    global total_posts, total_photos, total_messages, processed_accounts, remaining_accounts
    try:
        user_name, user_id, posts_count, photos_count, messages_count = get_user_info(token)

        with lock:
            total_posts += posts_count
            total_photos += photos_count
            total_messages += messages_count
            processed_accounts += 1
            remaining_accounts -= 1

            accounts_statistics.append({
                "name": user_name,
                "posts_count": posts_count,
                "photos_count": photos_count,
                "messages_count": messages_count
            })

        message_queue.put(('update_account_labels', None))
        message_queue.put(('update_total_items_labels', None))

    except Exception as e:
        message_queue.put(('log', f"Ошибка получения статистики для аккаунта: {e}\n"))

# Функция для отображения статистики
def show_statistics():
    global total_posts, total_photos, total_messages, processed_accounts, remaining_accounts
    total_posts, total_photos, total_messages = 0, 0, 0
    processed_accounts = 0
    remaining_accounts = total_accounts
    accounts_statistics.clear()
    message_queue.put(('update_account_labels', None))
    message_queue.put(('clear_statistics_text', None))
    message_queue.put(('log', "Начинается обновление статистики...\n"))

    threads = []
    for token in TOKENS:
        thread = threading.Thread(target=update_statistics_for_account, args=(token,))
        thread.start()
        threads.append(thread)

    for thread in threads:
        thread.join()

    accounts_statistics.sort(key=lambda x: x["posts_count"], reverse=True)

    for account in accounts_statistics:
        stats_message = (
            f"Пользователь: {account['name']}\n"
            f" - Количество постов: {account['posts_count']}\n"
            f" - Количество фотографий: {account['photos_count']}\n"
            f" - Количество сообщений: {account['messages_count']}\n\n"
        )
        message_queue.put(('statistics', stats_message))

    message_queue.put(('log', "Обновление статистики завершено.\n"))

# Функции для удаления данных
def delete_posts_for_account(token, count):
    global stop_flag, processed_accounts, remaining_accounts, total_posts
    try:
        user_name, user_id, posts_count, _, _ = get_user_info(token)
        message_queue.put(('log', f"Начинается очистка постов для пользователя {user_name}...\n"))

        posts_deleted = 0

        while True:
            if stop_flag or posts_deleted >= count:
                message_queue.put(('log', "Операция остановлена пользователем или достигнуто необходимое количество удалений.\n"))
                break

            posts_response = requests.get(
                'https://api.vk.com/method/wall.get',
                params={
                    'access_token': token,
                    'v': VERSION,
                    'owner_id': user_id,
                    'count': 100,
                    'offset': 0
                }
            ).json()

            if 'response' in posts_response:
                posts = posts_response['response']['items']
            else:
                message_queue.put(('log', f"Ошибка получения постов: {posts_response}\n"))
                break

            if not posts:
                break

            for post in posts:
                if stop_flag or posts_deleted >= count:
                    break
                post_id = post['id']
                attachments = post.get('attachments', [])

                # Удаляем фотографии, прикрепленные к посту
                delete_photos(attachments, token)

                delete_response = requests.get(
                    'https://api.vk.com/method/wall.delete',
                    params={
                        'access_token': token,
                        'v': VERSION,
                        'owner_id': user_id,
                        'post_id': post_id
                    }
                ).json()

                if 'response' in delete_response:
                    message_queue.put(('log', f"Пост {post_id} удален.\n"))
                    posts_deleted += 1

                    with lock:
                        total_posts -= 1
                        message_queue.put(('update_total_items_labels', None))
                else:
                    message_queue.put(('log', f"Ошибка удаления поста {post_id}: {delete_response}\n"))

                time.sleep(0.5)

            if not posts or posts_deleted >= count:
                break

        with lock:
            processed_accounts += 1
            remaining_accounts -= 1
            message_queue.put(('update_account_labels', None))

        message_queue.put(('log', f"Очистка постов для пользователя {user_name} завершена. Удалено постов: {posts_deleted}\n"))

    except Exception as e:
        message_queue.put(('log', f"Ошибка: {e}\n"))

def delete_all_photos_for_account(token):
    global stop_flag, processed_accounts, remaining_accounts, total_photos
    try:
        user_name, user_id, _, photos_count, _ = get_user_info(token)
        message_queue.put(('log', f"Начинается очистка всех фотографий для пользователя {user_name}...\n"))

        photos_deleted = 0

        while True:
            if stop_flag:
                message_queue.put(('log', "Операция остановлена пользователем.\n"))
                break
            photos_response = requests.get(
                'https://api.vk.com/method/photos.getAll',
                params={
                    'access_token': token,
                    'v': VERSION,
                    'owner_id': user_id,
                    'count': 200,
                    'offset': 0,
                }
            ).json()

            if 'response' not in photos_response:
                message_queue.put(('log', f"Ошибка получения фотографий: {photos_response}\n"))
                break

            photos = photos_response['response']['items']
            if not photos:
                break

            for photo in photos:
                if stop_flag:
                    break
                delete_response = requests.get(
                    'https://api.vk.com/method/photos.delete',
                    params={
                        'access_token': token,
                        'v': VERSION,
                        'owner_id': user_id,
                        'photo_id': photo['id']
                    }
                ).json()

                if 'response' in delete_response:
                    message_queue.put(('log', f"Фотография {photo['id']} удалена.\n"))
                    photos_deleted += 1

                    with lock:
                        total_photos -= 1
                        message_queue.put(('update_total_items_labels', None))
                else:
                    message_queue.put(('log', f"Ошибка удаления фотографии {photo['id']}: {delete_response}\n"))

                time.sleep(0.5)

            if not photos:
                break

        with lock:
            processed_accounts += 1
            remaining_accounts -= 1
            message_queue.put(('update_account_labels', None))

        message_queue.put(('log', f"Очистка фотографий для пользователя {user_name} завершена. Удалено фотографий: {photos_deleted}\n"))

    except Exception as e:
        message_queue.put(('log', f"Ошибка: {e}\n"))

def delete_all_messages_for_account(token):
    global stop_flag, processed_accounts, remaining_accounts, total_messages
    try:
        user_name, user_id, _, _, messages_count = get_user_info(token)
        message_queue.put(('log', f"Начинается очистка всех сообщений для пользователя {user_name}...\n"))

        chats_deleted = 0

        while True:
            if stop_flag:
                message_queue.put(('log', "Операция остановлена пользователем.\n"))
                break
            conversations_response = requests.get(
                'https://api.vk.com/method/messages.getConversations',
                params={
                    'access_token': token,
                    'v': VERSION,
                    'count': 200,
                    'offset': 0
                }
            ).json()

            if 'response' not in conversations_response:
                message_queue.put(('log', f"Ошибка получения бесед: {conversations_response}\n"))
                break

            conversations = conversations_response['response']['items']
            if not conversations:
                break

            for conversation in conversations:
                if stop_flag:
                    break
                peer_id = conversation['conversation']['peer']['id']

                delete_conversation_response = requests.get(
                    'https://api.vk.com/method/messages.deleteConversation',
                    params={
                        'access_token': token,
                        'v': VERSION,
                        'peer_id': peer_id
                    }
                ).json()

                if 'response' in delete_conversation_response:
                    message_queue.put(('log', f"Чат с ID {peer_id} удален.\n"))
                    chats_deleted += 1

                    with lock:
                        total_messages -= 1
                        message_queue.put(('update_total_items_labels', None))
                else:
                    error_code = delete_conversation_response.get('error', {}).get('error_code')
                    if error_code == 9:
                        message_queue.put(('log', f"Ошибка удаления чата с ID {peer_id}: Flood control. Ожидание перед повторной попыткой...\n"))
                        time.sleep(5)
                    else:
                        message_queue.put(('log', f"Ошибка удаления чата с ID {peer_id}: {delete_conversation_response}\n"))

                time.sleep(0.5)

            if not conversations:
                break

        with lock:
            processed_accounts += 1
            remaining_accounts -= 1
            message_queue.put(('update_account_labels', None))

        message_queue.put(('log', f"Очистка сообщений для пользователя {user_name} завершена. Удалено чатов: {chats_deleted}\n"))

    except Exception as e:
        message_queue.put(('log', f"Ошибка: {e}\n"))

def delete_photos(attachments, token):
    global total_photos
    for attachment in attachments:
        if stop_flag:
            message_queue.put(('log', "Операция остановлена пользователем.\n"))
            break
        if attachment['type'] == 'photo':
            photo_id = attachment['photo']['id']
            owner_id = attachment['photo']['owner_id']
            delete_response = requests.get(
                'https://api.vk.com/method/photos.delete',
                params={
                    'access_token': token,
                    'v': VERSION,
                    'owner_id': owner_id,
                    'photo_id': photo_id
                }
            ).json()

            if 'response' in delete_response:
                message_queue.put(('log', f"Фотография {photo_id} удалена.\n"))

                with lock:
                    total_photos -= 1
                    message_queue.put(('update_total_items_labels', None))
            else:
                message_queue.put(('log', f"Ошибка удаления фотографии {photo_id}: {delete_response}\n"))

            time.sleep(0.5)

# Функция для запуска очистки постов
def start_cleanup():
    global stop_flag, processed_accounts, remaining_accounts
    stop_flag = False
    processed_accounts = 0
    remaining_accounts = total_accounts
    update_account_labels()

    try:
        count = int(entry_count.get())
        if count <= 0:
            raise ValueError("Количество постов должно быть больше нуля.")
    except ValueError:
        messagebox.showerror("Ошибка", "Введите корректное количество постов.")
        return

    def cleanup_thread():
        threads = []
        for token in TOKENS:
            if stop_flag:
                message_queue.put(('log', "Операция остановлена пользователем.\n"))
                break
            thread = threading.Thread(target=delete_posts_for_account, args=(token, count))
            thread.start()
            threads.append(thread)

        for thread in threads:
            thread.join()

        message_queue.put(('log', "Очистка постов завершена.\n"))

        # Обновляем статистику после очистки
        show_statistics_thread()

    threading.Thread(target=cleanup_thread).start()

# Функция для остановки выполнения
def stop_cleanup():
    global stop_flag
    stop_flag = True

# Функция для удаления всех постов
def delete_all_posts():
    global stop_flag, processed_accounts, remaining_accounts
    stop_flag = False
    processed_accounts = 0
    remaining_accounts = total_accounts
    update_account_labels()

    def delete_all_posts_thread():
        threads = []
        for token in TOKENS:
            if stop_flag:
                message_queue.put(('log', "Операция остановлена пользователем.\n"))
                break
            thread = threading.Thread(target=delete_posts_for_account, args=(token, float('inf')))
            thread.start()
            threads.append(thread)

        for thread in threads:
            thread.join()

        message_queue.put(('log', "Удаление всех постов завершено.\n"))

        # Обновляем статистику после очистки
        show_statistics_thread()

    threading.Thread(target=delete_all_posts_thread).start()

# Функция для удаления всех фотографий
def delete_all_photos_for_all_tokens():
    global stop_flag, processed_accounts, remaining_accounts
    stop_flag = False
    processed_accounts = 0
    remaining_accounts = total_accounts
    update_account_labels()

    def delete_all_photos_thread():
        threads = []
        for token in TOKENS:
            if stop_flag:
                message_queue.put(('log', "Операция остановлена пользователем.\n"))
                break
            thread = threading.Thread(target=delete_all_photos_for_account, args=(token,))
            thread.start()
            threads.append(thread)

        for thread in threads:
            thread.join()

        message_queue.put(('log', "Удаление всех фотографий завершено.\n"))

        # Обновляем статистику после очистки
        show_statistics_thread()

    threading.Thread(target=delete_all_photos_thread).start()

# Функция для удаления всех сообщений
def delete_all_messages_for_all_tokens():
    global stop_flag, processed_accounts, remaining_accounts
    stop_flag = False
    processed_accounts = 0
    remaining_accounts = total_accounts
    update_account_labels()

    def delete_all_messages_thread():
        threads = []
        for token in TOKENS:
            if stop_flag:
                message_queue.put(('log', "Операция остановлена пользователем.\n"))
                break
            thread = threading.Thread(target=delete_all_messages_for_account, args=(token,))
            thread.start()
            threads.append(thread)

        for thread in threads:
            thread.join()

        message_queue.put(('log', "Удаление всех сообщений завершено.\n"))

        # Обновляем статистику после очистки
        show_statistics_thread()

    threading.Thread(target=delete_all_messages_thread).start()

# Функция для отображения статистики в отдельном потоке
def show_statistics_thread():
    threading.Thread(target=show_statistics).start()

# Функция для создания контекстного меню
def create_context_menu(text_widget, auto_scroll_var):
    menu = Menu(root, tearoff=0)
    menu.add_command(label="Копировать", command=lambda: root.clipboard_append(text_widget.get("sel.first", "sel.last")))
    menu.add_command(label="Выбрать все", command=lambda: text_widget.tag_add("sel", "1.0", "end"))
    menu.add_checkbutton(label="Автопрокрутка", onvalue=True, offvalue=False, variable=auto_scroll_var)

    def show_context_menu(event):
        menu.post(event.x_root, event.y_root)

    text_widget.bind("<Button-3>", show_context_menu)

# Функция для периодического обновления GUI из очереди
def process_queue():
    try:
        while True:
            task = message_queue.get_nowait()
            if task[0] == 'log':
                log_text.insert(tk.END, task[1])
                if auto_scroll_log.get():
                    log_text.yview(tk.END)
            elif task[0] == 'statistics':
                statistics_text.insert(tk.END, task[1])
                if auto_scroll_statistics.get():
                    statistics_text.yview(tk.END)
            elif task[0] == 'update_account_labels':
                update_account_labels()
            elif task[0] == 'update_total_items_labels':
                update_total_items_labels()
            elif task[0] == 'clear_statistics_text':
                statistics_text.delete('1.0', tk.END)
            message_queue.task_done()
    except queue.Empty:
        pass
    root.after(100, process_queue)

# Контейнер для виджетов
container = tk.Frame(root)
container.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)

# Поле для ввода количества постов
label_count = tk.Label(container, text="Количество постов для удаления:", font=("Arial", 12))
label_count.grid(row=0, column=0, sticky="w", pady=5, padx=5)

entry_count = tk.Entry(container, width=10, font=("Arial", 12))
entry_count.grid(row=0, column=1, pady=5, padx=5)

# Кнопки управления
button_start = tk.Button(container, text="Запустить", font=("Arial", 12), command=start_cleanup)
button_start.grid(row=0, column=2, pady=5, padx=5)

button_stop = tk.Button(container, text="Остановить", font=("Arial", 12), command=stop_cleanup)
button_stop.grid(row=0, column=3, pady=5, padx=5)

button_delete_all = tk.Button(container, text="Удалить все посты", font=("Arial", 12), command=delete_all_posts)
button_delete_all.grid(row=1, column=0, pady=5, padx=5, sticky="ew", columnspan=4)

button_delete_all_photos = tk.Button(container, text="Удалить все фотографии", font=("Arial", 12), command=delete_all_photos_for_all_tokens)
button_delete_all_photos.grid(row=2, column=0, pady=5, padx=5, sticky="ew", columnspan=4)

button_delete_all_messages = tk.Button(container, text="Удалить все сообщения", font=("Arial", 12), command=delete_all_messages_for_all_tokens)
button_delete_all_messages.grid(row=3, column=0, pady=5, padx=5, sticky="ew", columnspan=4)

# Метки для статистики аккаунтов
label_total_accounts = tk.Label(container, text="Количество аккаунтов в файле:", font=("Arial", 12, "bold"))
label_total_accounts.grid(row=5, column=0, sticky="w", pady=5, padx=5)

label_total_accounts_value = tk.Label(container, text=str(total_accounts), font=("Arial", 12, "bold"))
label_total_accounts_value.grid(row=5, column=2, sticky="w", pady=5, padx=5, columnspan=2)

label_processed_accounts = tk.Label(container, text="Количество отработанных аккаунтов:", font=("Arial", 12, "bold"))
label_processed_accounts.grid(row=6, column=0, sticky="w", pady=5, padx=5)

label_processed_accounts_value = tk.Label(container, text=str(processed_accounts), font=("Arial", 12, "bold"))
label_processed_accounts_value.grid(row=6, column=2, sticky="w", pady=5, padx=5, columnspan=2)

label_remaining_accounts = tk.Label(container, text="Количество оставшихся аккаунтов:", font=("Arial", 12, "bold"))
label_remaining_accounts.grid(row=7, column=0, sticky="w", pady=5, padx=5)

label_remaining_accounts_value = tk.Label(container, text=str(remaining_accounts), font=("Arial", 12, "bold"))
label_remaining_accounts_value.grid(row=7, column=2, sticky="w", pady=5, padx=5, columnspan=2)

# Метки для общей статистики
label_total_posts = tk.Label(container, text="Общее количество постов:", font=("Arial", 12, "bold"))
label_total_posts.grid(row=8, column=0, sticky="w", pady=5, padx=5)

label_total_posts_value = tk.Label(container, text=str(total_posts), font=("Arial", 12, "bold"))
label_total_posts_value.grid(row=8, column=2, sticky="w", pady=5, padx=5, columnspan=2)

label_total_photos = tk.Label(container, text="Общее количество фотографий:", font=("Arial", 12, "bold"))
label_total_photos.grid(row=9, column=0, sticky="w", pady=5, padx=5)

label_total_photos_value = tk.Label(container, text=str(total_photos), font=("Arial", 12, "bold"))
label_total_photos_value.grid(row=9, column=2, sticky="w", pady=5, padx=5, columnspan=2)

label_total_messages = tk.Label(container, text="Общее количество сообщений:", font=("Arial", 12, "bold"))
label_total_messages.grid(row=10, column=0, sticky="w", pady=5, padx=5)

label_total_messages_value = tk.Label(container, text=str(total_messages), font=("Arial", 12, "bold"))
label_total_messages_value.grid(row=10, column=2, sticky="w", pady=5, padx=5, columnspan=2)

# Поля вывода логов и статистики
log_text = scrolledtext.ScrolledText(container, width=70, height=8, font=("Arial", 10))
log_text.grid(row=11, column=0, pady=10, padx=5, columnspan=4)

statistics_text = scrolledtext.ScrolledText(container, width=70, height=8, font=("Arial", 10))
statistics_text.grid(row=12, column=0, pady=10, padx=5, columnspan=4)

# Добавление контекстного меню
create_context_menu(log_text, auto_scroll_log)
create_context_menu(statistics_text, auto_scroll_statistics)

# Инициализация меток аккаунтов
update_account_labels()

# Запуск обработки очереди сообщений
root.after(100, process_queue)

# Автоматическое отображение статистики при запуске
show_statistics_thread()

# Запуск главного цикла приложения
root.mainloop()
